import UIKit

enum ThemeTool {
    static func objc_swizzleSelector1(
        _ sel1: Selector,
        inClass1 cls1: AnyClass,
        withSelector2 sel2: Selector,
        inClass2 cls2: AnyClass
    ) {
        guard let m1 = class_getInstanceMethod(cls1, sel1),
              let m2 = class_getInstanceMethod(cls2, sel2) else {
            return
        }
        let isM1Added = class_addMethod(cls1, sel1, method_getImplementation(m2), method_getTypeEncoding(m2))
        if isM1Added {
            class_replaceMethod(cls2, sel2, method_getImplementation(m1), method_getTypeEncoding(m1))
        }
        else {
            method_exchangeImplementations(m1, m2)
        }
    }
}

extension UIView {
    private enum ASKey {
        static var tColorKey = "tColorKey"
        static var tColorValue = "tColorValue"
    }
    
    @objc class func themeableSetups() {
        ThemeTool.objc_swizzleSelector1(
            #selector(UIView.init(frame:)),
            inClass1: Self.self,
            withSelector2: #selector(self_init(frame:)),
            inClass2: Self.self
        )
        ThemeTool.objc_swizzleSelector1(
            #selector(awakeFromNib),
            inClass1: Self.self,
            withSelector2: #selector(self_awakeFromNib),
            inClass2: Self.self
        )
    }
    
    @IBInspectable
    var tColorKey: String {
        get {
            return objc_getAssociatedObject(self, &ASKey.tColorKey) as? String ?? ""
        }
        set{
            objc_setAssociatedObject(self, &ASKey.tColorKey, newValue, .OBJC_ASSOCIATION_COPY)
        }
    }
    
    @IBInspectable
    var tColorValue: String {
        get {
            return objc_getAssociatedObject(self, &ASKey.tColorValue) as? String ?? ""
        }
        set{
            objc_setAssociatedObject(self, &ASKey.tColorValue, newValue, .OBJC_ASSOCIATION_COPY)
        }
    }
    
    private var getXibThemedColor: UIColor? {
        let colorValue = tColorValue
        guard colorValue.contains(".") else {
            return nil
        }
        let components = colorValue.components(separatedBy: ".")
        guard components.count == 2 else {
            return nil
        }
        return DemoThemeColorStore.shared.findColor(
            forStyle: DemoTheme.shared.currentStyle,
            atScope: components[0],
            colorName: components[1]
        )
    }
    
    @objc func self_init(frame: CGRect) {
        self_init(frame: frame)

        guard let _ = self as? DemoThemeAdaptable else {
            return
        }
        themeStyleDidChange()
    }
    
    /// when self is `awakeFromNib` or `themeStyleDidChange`,
    /// then update setting the `theme configured semantic color`
    @objc func self_awakeFromNib() {
        self_awakeFromNib()
        
        setupThemeable()
    }
    
    func setupThemeable(){
        updateXibThemedColor()
        
        let view = self as? DemoThemeAdaptable
        guard view != nil || getXibThemedColor != nil else {
            return
        }
        NotificationCenter.default.addObserver(self, selector: #selector(themeStyleDidChange), name: .DemoThemeStyleDidChange, object: nil)
    }
    
    @objc func updateXibThemedColor() {
        guard
            !tColorKey.isEmpty,
            !tColorValue.isEmpty,
            let color = getXibThemedColor else {
            return
        }
        setValue(color, forKeyPath: tColorKey)
    }
    
    @objc func themeStyleDidChange() {
        updateXibThemedColor()
        
        (self as? DemoThemeAdaptable)?.updateThemeStyleIfNeeded()
    }
    
}
